package com.kymatrix.kycarbon.xsmd.kbpm.server.core.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kymatrix.kycarbon.xsmd.common.exception.BusinessException;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.enums.YesNoEnum;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.dto.ProcessDefineParam;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.engine.model.ProcessModel;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.engine.parser.ModelParser;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.enums.ProcessDefineStateEnum;
import com.kymatrix.kycarbon.xsmd.kbpm.server.core.service.ProcessDefineService;
import com.kymatrix.kycarbon.xsmd.kbpm.server.dao.ProcessDefineMapper;
import com.kymatrix.kycarbon.xsmd.kbpm.server.dao.ProcessInstanceMapper;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.entity.ProcessDefineEntity;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.entity.ProcessInstanceEntity;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.form.UpAndDownParam;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.form.define.PageQueryProcessDefineReq;
import com.kymatrix.kycarbon.xsmd.kbpm.server.model.form.define.PageQueryProcessDefineResp;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * <p>
 * 流程定义 服务实现类
 * </p>
 */
@Service
@RequiredArgsConstructor
public class ProcessDefineServiceImpl extends ServiceImpl<ProcessDefineMapper, ProcessDefineEntity> implements ProcessDefineService {
    private final ProcessInstanceMapper processInstanceMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(ProcessDefineParam param) {
        param.setId(null);
        ProcessDefineEntity processDefineEntity = new ProcessDefineEntity();
        BeanUtil.copyProperties(param, processDefineEntity);
        return super.save(processDefineEntity);
    }

    @Override
    public boolean update(ProcessDefineParam param) {
        ProcessDefineEntity processDefineEntity = new ProcessDefineEntity();
        BeanUtil.copyProperties(param, processDefineEntity);
        return super.updateById(processDefineEntity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeBatchByIds(Collection<?> list) {
        list.forEach(id->{
           long count = processInstanceMapper.selectCount(Wrappers.lambdaQuery(ProcessInstanceEntity.class).eq(ProcessInstanceEntity::getProcessDefineId,id));
           if(count>0) {
               throw new BusinessException("存在正在未完成的流程实例，不允许删除！");
           }
        });
        return super.removeBatchByIds(list);
    }

    @Override
    public Page<PageQueryProcessDefineResp> page(PageQueryProcessDefineReq param) {
        IPage<PageQueryProcessDefineResp> page = new Page<>(param.getCurrent(), param.getPageSize());
        return baseMapper.pageQuery(page, param);
    }
    @Override
    public PageQueryProcessDefineResp findById(Long id) {
        PageQueryProcessDefineResp vo =  baseMapper.findById(id);
        if(vo!=null) {
            vo.setContent(null);
            vo.setJsonObject(getDefineJsonObject(id));
        }
        return vo;
    }
    @Override
    public Long deploy(InputStream inputStream) {
        return deploy(IoUtil.readBytes(inputStream));
    }

    @Override
    public Long deploy(byte[] bytes) {
        Date now = new Date();
        // 1. json定义文件转成流程模型
        ProcessModel processModel = ModelParser.parse(bytes);
        // 2. 根据名称查询，取最新版本的流程定义记录
        List<ProcessDefineEntity> processDefineEntityList = baseMapper.selectList(
                Wrappers.lambdaQuery(ProcessDefineEntity.class)
                        .eq(ProcessDefineEntity::getName,processModel.getName())
                        .orderByDesc(ProcessDefineEntity::getId)
        );
        ProcessDefineEntity processDefineEntity = null;
        // 3.1 如果存在，则版本+1，并插入一条新的流程定义记录
        if(!processDefineEntityList.isEmpty()) {
            processDefineEntity = processDefineEntityList.get(0);
            processDefineEntity.setId(null);
            processDefineEntity.setVersion(processDefineEntity.getVersion()+1);
        } else {
            // 3.2 如果不存在，则版本默认为1，并插入一条新的流程定义记录
            processDefineEntity = new ProcessDefineEntity();
            processDefineEntity.setVersion(0);
        }
        processDefineEntity.setName(processModel.getName());
        processDefineEntity.setDisplayName(processModel.getDisplayName());
        processDefineEntity.setType(processModel.getType());
        processDefineEntity.setCreateTime(now);
        processDefineEntity.setUpdateTime(now);
        processDefineEntity.setState(ProcessDefineStateEnum.ENABLE.getCode());
        processDefineEntity.setContent(bytes);
        baseMapper.insert(processDefineEntity);
        return processDefineEntity.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long deploy(String json) {
        return deploy(json.getBytes());
    }

    @Override
    public void redeploy(Long processDefineId, InputStream inputStream) {
        redeploy(processDefineId,IoUtil.readBytes(inputStream));
    }

    @Override
    public void redeploy(Long processDefineId, byte[] bytes) {
        Date now = new Date();
        // 1. json定义文件转成流程模型
        ProcessModel processModel = ModelParser.parse(bytes);
        // 2. 构造模型定义信息
        ProcessDefineEntity processDefineEntity = new ProcessDefineEntity();
        processDefineEntity.setId(processDefineId);
        processDefineEntity.setType(processModel.getType());
        processDefineEntity.setName(processModel.getName());
        processDefineEntity.setDisplayName(processModel.getDisplayName());
        processDefineEntity.setUpdateTime(now);
        processDefineEntity.setContent(bytes);
        // 3. 更新模型定义文件
        baseMapper.updateById(processDefineEntity);
    }

    @Override
    public void redeploy(Long processDefineId, String json) {
        redeploy(processDefineId, json.getBytes());
    }

    @Override
    public void unDeploy(Long processDefineId) {
        ProcessDefineEntity processDefineEntity = new ProcessDefineEntity();
        processDefineEntity.setId(processDefineId);
        processDefineEntity.setState(ProcessDefineStateEnum.DISABLE.getCode());
        baseMapper.updateById(processDefineEntity);
    }

    @Override
    public void reDeploy(Long processDefineId) {
        ProcessDefineEntity processDefineEntity = new ProcessDefineEntity();
        processDefineEntity.setId(processDefineId);
        processDefineEntity.setState(ProcessDefineStateEnum.ENABLE.getCode());
        baseMapper.updateById(processDefineEntity);
    }

    @Override
    public void updateType(Long processDefineId, String type) {
        ProcessDefineEntity processDefineEntity = new ProcessDefineEntity();
        processDefineEntity.setId(processDefineId);
        processDefineEntity.setType(type);
        baseMapper.updateById(processDefineEntity);
    }

    @Override
    public ProcessDefineEntity getById(Long processDefineId) {
        return baseMapper.selectById(processDefineId);
    }

    @Override
    public ProcessModel getProcessModel(Long processDefineId) {
        return processDefineToModel(getById(processDefineId));
    }

    @Override
    public ProcessModel processDefineToModel(ProcessDefineEntity processDefineEntity) {
        if(processDefineEntity == null) return null;
        ProcessModel processModel = ModelParser.parse(processDefineEntity.getContent());
        // 注：后续再进行缓存优化，减少频繁解析
        return processModel;
    }

    @Override
    public String getDefineJsonStr(Long id) {
        ProcessDefineEntity processDefineEntity = getById(id);
        if(processDefineEntity == null) return null;
        return StrUtil.str(processDefineEntity.getContent(), Charset.defaultCharset());
    }

    @Override
    public JSONObject getDefineJsonObject(Long processDefineId) {
        return JSONUtil.parseObj(Convert.toStr(getDefineJsonStr(processDefineId),"{}"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void upAndDown(UpAndDownParam param) {
        if(YesNoEnum.YES.equals(param.getOpType())) {
            param.getIds().forEach(id->{
                reDeploy(Convert.toLong(id));
            });
        } else {
            param.getIds().forEach(id->{
                unDeploy(Convert.toLong(id));
            });
        }
    }

    @Override
    public PageQueryProcessDefineResp getLastByName(String name) {
        return baseMapper.selectLastByName(name);
    }
    @Override
    public ProcessDefineEntity getProcessDefineByVersion(String name, Integer version) {
        LambdaQueryWrapper<ProcessDefineEntity> queryWrapper = Wrappers.lambdaQuery(ProcessDefineEntity.class);
        queryWrapper.eq(ProcessDefineEntity::getName,name);
        if(version!=null) {
            queryWrapper.eq(ProcessDefineEntity::getVersion, version);
        }
        queryWrapper.orderByDesc(ProcessDefineEntity::getId);
        List<ProcessDefineEntity> processDefineEntityList = baseMapper.selectList(queryWrapper);
        if(!processDefineEntityList.isEmpty()) {
            return processDefineEntityList.get(0);
        }
        return null;
    }
}
